package com.xiaoc.bulldozer.springboot.config.web;

import com.xiaoc.bulldozer.springboot.config.wrapper.XiaocResponseWrapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * @author wj on 2019/5/14.
 */
public class XiaocCommonsRequestLoggingFilter extends OncePerRequestFilter {

    private static final int DEFAULT_MAX_PAYLOAD_LENGTH = 500;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 将response 先写入responseWrapper(此时response中的流已经被消费没有数据了) 然后 response 再输出给前端
     * 实际流程 freemarker -> call getWriter -> write to responseWrapper(原来是写入tomcat的response) -> response  write content
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        HttpServletRequest requestToUse = request;

        long startTime = System.currentTimeMillis();
        boolean isFirstRequest = !isAsyncDispatch(request);
        if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
            requestToUse = new ContentCachingRequestWrapper(request, DEFAULT_MAX_PAYLOAD_LENGTH);
        }

        XiaocResponseWrapper responseWrapper = new XiaocResponseWrapper(response);
        filterChain.doFilter(requestToUse, responseWrapper);
        // getInputStream 并且读取数据后才会填充ContentCachingRequestWrapper 中的数据才能获取到客户端信息
        String inputParamInfo = createMessage(requestToUse);
        byte[] bytes = responseWrapper.getBytes();
        response.getOutputStream().write(bytes);
        response.setHeader("Content-Type", "application/json;charset=utf-8");

        String responseStr = new String(bytes);
        String successKey = "\"code\" : 200";
        long costTime = System.currentTimeMillis() - startTime;
        if(!StringUtils.contains(responseStr,successKey)){
            logger.warn("method: {}, url: {}, client param: [{}], response content: [{}], cost:[{}]ms ",request.getMethod(), request.getRequestURI(), inputParamInfo, responseStr, costTime);
        }else{
            logger.info("method: {}, url: {}, client param: [{}], cost:[{}]ms ",request.getMethod(), request.getRequestURI(), inputParamInfo, costTime);
        }

    }


    protected String createMessage(HttpServletRequest request) {
        StringBuilder msg = new StringBuilder();

        if (isIncludePayload()) {
            String payload = getMessagePayload(request);
            if (payload != null) {
                msg.append(" payload=").append(payload);
            }
        }

        if (isIncludeParameters()) {
            Map<String,Object > parameter = WebUtils.getParametersStartingWith(request, "");
            if (parameter != null && !parameter.isEmpty()) {
                msg.append(" parameter= ").append(parameter);
            }
        }

        return msg.toString();
    }

    @Nullable
    protected String getMessagePayload(HttpServletRequest request) {
        ContentCachingRequestWrapper wrapper =
                WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                int length = Math.min(buf.length, DEFAULT_MAX_PAYLOAD_LENGTH);
                try {
                    return new String(buf, 0, length, wrapper.getCharacterEncoding());
                }
                catch (UnsupportedEncodingException ex) {
                    return "[unknown]";
                }
            }
        }
        return null;
    }

    protected boolean isIncludePayload() {
        return true;
    }

    protected boolean isIncludeParameters() {
        return true;
    }



}
